﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ThreadTest
{
    class Program
    {
        static void Main(string[] args)
        {

            //Test2();

            //TestBarrier();
            //TestBarrier2();

            //InterlockedTest();

            //ManualResetEventTest();

            //MutexTest();

            //SemaphoreTest();

            //ThreadJoinTest();

            Console.ReadLine();
        }

        private static void ThreadJoinTest()
        {
            var local = Thread.AllocateDataSlot();
            Thread.SetData(local, "Main thread init value");

            var thread = new Thread(() =>
            {
                Console.WriteLine("{0} - ThreadId-{1} running.",
                    DateTime.Now,
                    Thread.CurrentThread.ManagedThreadId);


                Thread.SetData(local, "Worker thread set value");

                Thread.Sleep(3000);


                var val = Thread.GetData(local);
                Console.WriteLine("{0} -ThreadId-{1}: {2}", DateTime.Now,
                    Thread.CurrentThread.ManagedThreadId,
                    val);
            });

            thread.Start();
            while (!thread.IsAlive) ;

            thread.Join();

            Console.WriteLine("{0} - Main thread-{1} running.",
                   DateTime.Now,
                   Thread.CurrentThread.ManagedThreadId);

            var mainValue = Thread.GetData(local);
            Console.WriteLine("{0} -Main thread-{1}: {2}", DateTime.Now,
                Thread.CurrentThread.ManagedThreadId,
                mainValue);
        }

        private static void SemaphoreTest()
        {
            var pool = new Semaphore(0, 3);
            var tasks = new Task[5];
            for (var i = 0; i <= 4; i++)
            {
                tasks[i] = Task.Factory.StartNew((num) =>
                {
                    var taskId = "Task-" + num;

                    Console.WriteLine("#{0} begins and waits for semaphore.", taskId);
                    pool.WaitOne();

                    Console.WriteLine("\t{0} enter semaphore.", taskId);

                    Thread.Sleep(1000 * (int)num);

                    Console.WriteLine("@{0} release semaphore.", taskId);

                    pool.Release();

                }, i + 1);
            }

            Console.WriteLine("Main thread sleep 2000ms");
            Thread.Sleep(2000);

            Console.WriteLine("Main thread calls release(3)");
            pool.Release(2);
        }

        private static void MutexTest()
        {
            var tasks = new Task[5];
            var mutex = new Mutex();
            var rnd = new Random();
            for (var i = 0; i <= 4; i++)
            {
                tasks[i] = Task.Factory.StartNew((num) =>
                {
                    var taskId = "Task-" + num;
                    Console.WriteLine("#{0} attempts acquire mutex.", taskId);
                    mutex.WaitOne();
                    Console.WriteLine("\t{0} enter mutex", taskId);

                    var sleepMs = rnd.Next(1, 10) * 1000;
                    Console.WriteLine("\t{0} sleep {1}ms", taskId, sleepMs);
                    Thread.Sleep(sleepMs);


                    mutex.ReleaseMutex();
                    Console.WriteLine("#{0} release mutex", taskId);
                }, i + 1);
            }
        }

        private static void ManualResetEventTest()
        {
            var mre = new ManualResetEvent(false);
            var task = Task.Run(() =>
            {
                Console.WriteLine("Task is mre waiting, please input a word");
                mre.WaitOne();

                Console.WriteLine("You input A, task sleep 2000 ms, and reset mre.");

                mre.Reset();
                mre.Reset();
                Thread.Sleep(5000);

                Console.WriteLine("Task terminated, and set mer");
                mre.Set();

            });

            var keyword = Console.ReadKey();
            while (keyword.Key != ConsoleKey.A)
            {
                keyword = Console.ReadKey();
            }

            var setStatus = mre.Set();
            Console.WriteLine("Main thread mre.Set result=" + setStatus);

            Console.WriteLine("Main thread try wait");
            mre.WaitOne();

            Console.WriteLine("Main thread terminated.");


        }

        private static int count = 0;


        private static void InterlockedTest()
        {
            var usingResource = 0;
            var index = 0;

            var taskNum = 2;
            for (var i = 0; i < taskNum; i++)
            {
                var t = new Thread(() =>
                {
                    for (var n = 0; n < 5; n++)
                    {
                        Interlocked.Increment(ref index);
                        Console.WriteLine("\t {0} Increment, Index = {1}",
                                Thread.CurrentThread.Name, index);
                        //if(Interlocked.Increment(ref index) - index == 1){
                        //    Console.WriteLine("\t {0} Increment ok", 
                        //        Thread.CurrentThread.Name);
                        //}
                        if (0 == Interlocked.Exchange(ref usingResource, 1))
                        {
                            Console.WriteLine("   {0} acquired the lock",
                                Thread.CurrentThread.Name);
                            Thread.Sleep(500);
                            Console.WriteLine("   {0} exiting lock", Thread.CurrentThread.Name);

                            //Release the lock
                            Interlocked.Exchange(ref usingResource, 0);
                        }
                        else
                        {
                            Console.WriteLine("   {0} was denied the lock", Thread.CurrentThread.Name);
                        }
                        Thread.Sleep(1000);
                    }
                });
                t.Name = "Thread-" + i;
                t.Start();
            }


        }


        private static void TestBarrier2()
        {
            var taskNum = 4;
            var tasks = new List<Task>();
            var barrier = new Barrier(5, (b) =>
            {
                Console.WriteLine("================== Phase = {0}", b.CurrentPhaseNumber);
            });

            barrier.RemoveParticipant();

            Enumerable.Range(1, taskNum)
                .ToList()
                .ForEach(n =>
                {
                    var t = Task.Factory.StartNew((num) =>
                    {
                        var taskId = (int)num;
                        Console.WriteLine("#TaskId={0} Op1 Done", taskId);
                        barrier.SignalAndWait();

                        Console.WriteLine("#TaskId={0} Op2 Done", taskId);
                        barrier.SignalAndWait();

                        Console.WriteLine("#TaskId={0} Op3 Done", taskId);
                        barrier.SignalAndWait();

                    }, n);
                    tasks.Add(t);
                });


            var finalTask = Task.Factory.ContinueWhenAll(tasks.ToArray(), taskList =>
            {
                Task.WaitAll(taskList);
                Console.WriteLine("========================================");
                Console.WriteLine("All Phase is completed");
            });


            finalTask.Wait();

            barrier.Dispose();
        }

        private static void TestBarrier()
        {
            var barrier = new Barrier(3, (b) =>
            {
                Console.WriteLine("Post-phase Action: count={0}, number={1}",
                    count, b.CurrentPhaseNumber);

                if (b.CurrentPhaseNumber == 2)
                {
                    throw new Exception("2 throw a exception.");
                }
            });

            barrier.AddParticipants(2);

            barrier.RemoveParticipant();

            Action action = () =>
            {
                Interlocked.Increment(ref count);
                barrier.SignalAndWait(); // during the post-phase action, count should be 4 and phase should be 0
                Interlocked.Increment(ref count);
                barrier.SignalAndWait(); // during the post-phase action, count should be 8 and phase should be 1

                // The third time, SignalAndWait() will throw an exception and all participants will see it
                Interlocked.Increment(ref count);
                try
                {
                    barrier.SignalAndWait();
                }
                catch (BarrierPostPhaseException bppe)
                {
                    Console.WriteLine("Caught BarrierPostPhaseException: {0}", bppe.Message);
                }

                // The fourth time should be hunky-dory
                Interlocked.Increment(ref count);
                barrier.SignalAndWait(); // during the post-phase action, count should be 16 and phase should be 3
            };

            // Now launch 4 parallel actions to serve as 4 participants
            Parallel.Invoke(action, action, action, action);

            // This (5 participants) would cause an exception:
            // Parallel.Invoke(action, action, action, action, action);
            //      "System.InvalidOperationException: The number of threads using the barrier
            //      exceeded the total number of registered participants."

            // It's good form to Dispose() a barrier when you're done with it.
            barrier.Dispose();
        }

        static void test2()
        {
            Worker workerObject = new Worker();
            Thread workerThread = new Thread(workerObject.DoWork);

            // Start the worker thread.
            workerThread.Start();
            Console.WriteLine("Main thread: starting worker thread...");

            // Loop until the worker thread activates.
            while (!workerThread.IsAlive)
                ;

            // Put the main thread to sleep for 1 millisecond to
            // allow the worker thread to do some work.
            Thread.Sleep(1000);

            // Request that the worker thread stop itself.
            workerObject.RequestStop();

            // Use the Thread.Join method to block the current thread 
            // until the object's thread terminates.
            workerThread.Join();
            Console.WriteLine("Main thread: worker thread has terminated.");
        }

        static void Test2()
        {
            TestDone done = new TestDone();
            var workThread = new Thread(done.DoWork);
            workThread.Start();
            while (!workThread.IsAlive) ;

            Console.WriteLine("Main thread Receive Keywork");

            var keywork = Console.ReadLine();
            while (keywork != "s")
            {
                Console.WriteLine("Main thread Receive Keywork");
                keywork = Console.ReadLine();
            }

            done.RequestStop();

            workThread.Join();

            Console.WriteLine("main thread wait ok");

            Console.ReadLine();
        }
    }

    public class TestDone
    {
        private bool _shouldStop = false;
        public void DoWork()
        {
            Console.WriteLine("DoWork...Before");
            while (!_shouldStop)
            {
                Console.WriteLine("DoWork ....");
                //Thread.Sleep(1000);
            }
        }

        public void RequestStop()
        {
            _shouldStop = true;
        }
    }

    public class Worker
    {
        // This method is called when the thread is started.
        public void DoWork()
        {
            while (!_shouldStop)
            {
                Thread.Sleep(500);
                Console.WriteLine("Worker thread: working...");
            }
            Console.WriteLine("Worker thread: terminating gracefully.");
        }
        public void RequestStop()
        {
            _shouldStop = true;
        }
        // Keyword volatile is used as a hint to the compiler that this data
        // member is accessed by multiple threads.
        private bool _shouldStop;
    }


    public class Counter
    {
        public static int Count { get; set; }

        private object locker = new object();

        private int _count;

        private int _count2;

        public void IncreaseOld()
        {
            _count++;
        }

        public void IncreaseVolatile()
        {
            ++_count2;
        }

        public void IncreaseWithLock()
        {
            lock (locker)
            {
                _count++;
            }
        }

        public void IncreaseWithInterlocked()
        {
            Interlocked.Increment(ref _count);
        }

        public void Display(string msg)
        {
            Console.WriteLine(msg + "_count = " + _count);
        }

        public void Display2(string msg)
        {
            Console.WriteLine(msg + "_count2 = " + _count2);
        }
    }


}
